#include "iConfig.hpp"

#include <glf/net/socket.h>

#if __APPLE__
	#include "socket_posix_.hpp"
#else
	#include GLFi_HEADER_API(socket, GLF_API_SOCKET)
#endif


namespace glf {

Socket::Socket() : 
	m(GLF_NEW_IMPL(*this))
{
	mLastError = NETERROR_NONE;
	mIsOpened = false;
	mConnectedCount = 0;
	
	GLF_CHECK_IMPL_SIZE();
}

Socket::~Socket() {
	Close();

	GLF_DELETE_IMPL();
}

void Socket::Close() {
	m.Close();
	mConnectedCount = 0;
}

NetError Socket::GetLastError() const {
	return mLastError;
}

void Socket::ClearError() {
	mLastError = NETERROR_NONE;
}

bool Socket::IsOpened() const {
	return mIsOpened;
}

bool Socket::OpenTcp(Option option) {
	Close();
	
	mType = TYPE_STREAM;
	mProtocol = PROTOCOL_TCP;
	mOptions = option;

	mIsOpened =  m.Open(TYPE_STREAM, PROTOCOL_TCP, option);

	return mIsOpened;
}

bool Socket::OpenUdp(Option option) {
	Close();

	mType = TYPE_DATAGRAM;
	mProtocol = PROTOCOL_UDP;
	mOptions = option;
	
	mIsOpened = m.Open(TYPE_DATAGRAM, PROTOCOL_UDP, option);

	return mIsOpened;
}

bool Socket::Open(Type type, Proto protocol, Option option) {
	Close();

	mProtocol = protocol;
	mType = type;
	mOptions = option;

	mIsOpened = m.Open(type, protocol, option);

	return mIsOpened;
}

bool Socket::Bind(const AddrIp4& addr) {
	return m.Bind(addr);
}

bool Socket::Connect(const AddrIp4& addr) {
	if (m.Connect(addr)) {
		mConnectedCount++;
		return true;
	}
	return false;
}

bool Socket::IsConnected() const
{
	return mLastError <= NETERROR_NONE && mConnectedCount > 0;
}

bool Socket::Listen(int backlog) {
	return m.Listen(backlog);
}

int Socket::Accept(AddrIp4& addr) {
	int socketIndex = m.Accept(addr);
	if (socketIndex != -1)
		mConnectedCount++;
	return socketIndex;
}

bool Socket::Reject(int id) {
	if (m.Reject(id)) {
		mConnectedCount--;
		return true;
	}
	return false;
}

int Socket::Send(const void* buf, int size, int id) {
	if(!mIsOpened) {
		mLastError = NETERROR_NOTOPENED;
		return false;
	}

	return m.Send(buf, size, id);
}

int Socket::SendTo(const AddrIp4& addr, const void* buf, int size) {
	if(!mIsOpened) {
		mLastError = NETERROR_NOTOPENED;
		return false;
	}

	return m.SendTo(addr, buf, size);
}

bool Socket::IsReadable(int id) const
{
	if(!mIsOpened) {
		mLastError = NETERROR_NOTOPENED;
		return false;
	}
	return m.IsReadable(id);
}

int Socket::Receive(void* buf, int size, int id) {
	if(!mIsOpened) {
		mLastError = NETERROR_NOTOPENED;
		return false;
	}
	return m.Receive(buf, size, id);
}

int Socket::ReceiveFrom(AddrIp4& addr, void* buf, int size) {
	if(!mIsOpened) {
		mLastError = NETERROR_NOTOPENED;
		return false;
	}
	return m.ReceiveFrom(addr, buf, size);
}

bool Socket::GetAddress(AddrIp4& addr) {
	return m.GetAddress(addr);
}

}

